package ilender.util;

import lombok.*;

import com.google.gson.*;
import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.*;

import ilender.facebook.*;
import ilender.serialization.*;

/**
 * Tools for easier handling of JSON.
 */
public class JsonTools{
	/*Instantiation Guard*/private JsonTools(){}

	/**
	 * Adds a string property to every entry in a JSON array.
	 *
	 * @param addTo The array to add the property to.
	 * @param name The name of the new property.
	 * @param value The value of the new property.
	 * @return The array.
	 */
	public static JsonArray addToEach(JsonArray addTo,String name,String value){
		for(val entry:addTo){
			entry.getAsJsonObject().addProperty(name,value);
		}
		return addTo;
	}

	public static JsonArray addToEach(JsonArray addTo,String name,boolean value){
		for(val entry:addTo){
			entry.getAsJsonObject().addProperty(name,value);
		}
		return addTo;
	}

	private static GsonBuilder gsonBuilder;

	/**Creates a Gson object.*/
	public static Gson createGson(){
		if(null==gsonBuilder){
			synchronized(JsonTools.class){
				if(null==gsonBuilder){
					gsonBuilder=new GsonBuilder();
					gsonBuilder.registerTypeAdapter(DateTime.class,new DateTimeTypeConverter());
					gsonBuilder.registerTypeAdapter(DateMidnight.class,new DateMidnightTypeConverter());
				}
			}
		}
		return gsonBuilder.create();
	}

	/**Turns a java object to a JSON element.*/
	public static JsonElement makeElement(Object source){
		if(source instanceof JsonElement)
			return (JsonElement)source;
		else if(source instanceof String)
			return new JsonPrimitive((String)source);
		else if(source instanceof Number)
			return new JsonPrimitive((Number)source);
		else if(source instanceof Character)
			return new JsonPrimitive((Character)source);
		else
			return createGson().toJsonTree(source);
	}

	/**
	 * Creates a JSON object.
	 *
	 * @param args Property names, each followed by it's value.
	 * @return The new JSON object.
	 */
	public static JsonObject makeObject(Object...args){
		assert(0==args.length%2);
		JsonObject result=new JsonObject();
		for(int i=0;i<args.length;i+=2){
			result.add(args[i].toString(),makeElement(args[i+1]));
		}
		return result;
	}

	/**
	 * Creates a JSON array.
	 *
	 * @param args The array's entries.
	 * @return The new JSON array.
	 */
	public static JsonArray makeArray(Object...args){
		JsonArray result=new JsonArray();
		for(val arg:args){
			result.add(makeElement(arg));
		}
		return result;
	}

	/**
	 * Creates a JSON array.
	 *
	 * @param source The array's entries.
	 * @return The new JSON array.
	 */
	public static JsonArray makeArrayFromIterable(Iterable source){
		JsonArray result=new JsonArray();
		for(val arg:source){
			result.add(makeElement(arg));
		}
		return result;
	}

	/**
	 * Turns an iterable to a JSON array result of JQuery's autocomplete format.
	 *
	 * @param source The result for the autocomplete query.
	 * @param valueProperty the name of the JSON "value" property.
	 * @param labelProperty the name of the JSON "label" property.
	 * @return A JSON array for the autocomplete result.
	 */
	public static JsonArray makeAutocompleteEntriesArray(Iterable source,String valueProperty,String labelProperty){
		try{
			JsonArray result=new JsonArray();
			for(Object sourceEntry:source){
				JsonObject resultEntry=new JsonObject();
				resultEntry.addProperty("value",BeanUtils.getSimpleProperty(sourceEntry,valueProperty));
				resultEntry.addProperty("label",BeanUtils.getSimpleProperty(sourceEntry,labelProperty));
				result.add(resultEntry);
			}
			return result;
		}catch(Exception e){
			throw new Error(e);
		}
	}

	public static JsonArray makeAutocompleteEntriesArray(Iterable source,String labelProperty){
		return makeAutocompleteEntriesArray(source,"id",labelProperty);
	}

	/**
	 * Duplicate a JSON array, filtering out certain entries.
	 *
	 * @param source The array.
	 * @param fieldName The field to check.
	 * @param ignoreIf The value to check against.
	 * @return The array, without the entries where fieldName == ignoreIf.
	 */
	public static JsonArray duplicateArrayWhileIgnoreing(JsonArray source,String fieldName,JsonPrimitive ignoreIf){
		JsonArray result=new JsonArray();
		for(JsonElement entry:source){
			if(!(
						entry.isJsonObject()
						&&
						ignoreIf.equals(entry.getAsJsonObject().get(fieldName))
				)){
				result.add(entry);
				}
		}
		return result;
	}
}
